package modularity.passiveDrift;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class EvolutionEngine {


	/**
	 * @param args
	 * @throws Exception 
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws Exception {
		System.out.println("BASIC GENOME SETUP");
		new EvolutionEngine("modularity_confs/drift_params.txt").execute();
		System.out.println("==========================\n\n");
		
		System.out.println("BASIC GENOME SETUP WITH GENE DUPLICATION");
		new EvolutionEngine("modularity_confs/drift_params_simple_gene_duplication.txt").execute();
		System.out.println("==========================");
		System.out.println("ENDED...");
	}


	protected Properties arguments;

	public EvolutionEngine(String paramsFile) throws FileNotFoundException, IOException {
		arguments = new Properties();
		arguments.load(new FileReader(paramsFile));
	}

	private void execute() throws Exception {
		final long startTime = System.currentTimeMillis()/1000;

		/**
		 * reading a couple of arguments
		 */
		String genomeType = arguments.getProperty("genome", "BasicGenome");
		int populationSize = Integer.valueOf(arguments.getProperty("population-size", "500"));
		int inputs = Integer.valueOf(arguments.getProperty("input-nodes", "5"));
		int outputs = Integer.valueOf(arguments.getProperty("output-nodes", "3"));

		int generations = Integer.valueOf(arguments.getProperty("generations", "1000"));
		int runs = Integer.valueOf(arguments.getProperty("runs", "1"));

		String statisticsFolder = arguments.getProperty("statistics-folder", "modularity-test/");

		int evaluationThreads = Integer.valueOf(arguments.getProperty("evaluation-threads", "2"));
		int mutationThreads = Integer.valueOf(arguments.getProperty("mutation-threads", "2"));
		if(evaluationThreads <= 0)
			evaluationThreads = Runtime.getRuntime().availableProcessors();
		if(mutationThreads <= 0)
			mutationThreads = Runtime.getRuntime().availableProcessors();

		boolean geneDuplication = Boolean.valueOf(arguments.getProperty("node-duplication", "false"));

		/**
		 * done with the parameters, start with the initialisation
		 */

		System.out.println("===== EVOLUTION ENGINE SETTINGS =====");
		System.out.println("POPULATION SIZE: " + populationSize);
		System.out.println("GENERATIONS: " + generations);
		System.out.println("RUNS: " + runs);
		System.out.println("EVALUATION THREADS: " + evaluationThreads);
		System.out.println("MUTATION THREADS: " + mutationThreads);
		System.out.println("NODE DUPLICATION: " + geneDuplication);
		System.out.println("=====================================");


		/**
		 * JMOD SETTINGS
		 */

		/*JmodSettings settings = JmodSettings.getInstance();
		//settings.setNumProcessors(evaluationThreads);
		settings.setModularityDetectionMethod("Newman");
		settings.setExportBasicDataset(true);
		settings.setCommunityNetworkFormat(Structure.GML);
		settings.setExportColoredCommunities(true);
		JmodSettings.setLogLevel("NONE");*/

		FileManager fileManager = new FileManager(statisticsFolder);
		fileManager.saveArguments(arguments);

		DiskStorage storage = new DiskStorage();
		storage.setNumberOfThreads(evaluationThreads);

		for(int run = 1; run <= runs; run++){
			System.out.println("RUN " + run);
			BasicMutator.setNodeId(inputs+outputs);

			fileManager.setRun(run);

			ListedPopulation population = initialisePopulation(genomeType, populationSize, inputs, outputs);
			MutationManager mutator = initialiseMutator(genomeType, geneDuplication);
			mutator.setMutationThreads(mutationThreads);	


			String currentOutputDirectory = fileManager.getOutputDirectory(0);
			storage.store(population, currentOutputDirectory);

			for(int generation = 1; generation <= generations; generation++){
				mutator.mutate(population);
				currentOutputDirectory = fileManager.getOutputDirectory(generation);
				storage.store(population, currentOutputDirectory);
			}

			System.out.println("RUN : " + run + "/" + runs + " done.");
			processResults(generations, run, populationSize, fileManager);

			computeEta(startTime, run, runs);
		}
		System.out.println("Done.");
		System.out.println(formatIntoHHMMSS((System.currentTimeMillis()/1000) - startTime));
	}

	private void processResults(int generations, int run, int populationSize, FileManager manager) {
		// TODO Auto-generated method stub

	}

	private void computeEta(long startTime, int runsCompleted, int totalRuns) {
		long averageTime = ((System.currentTimeMillis()/1000) - startTime)/runsCompleted;
		long missingTime = (totalRuns - runsCompleted) * averageTime;
		System.out.println("===========================================================================");
		System.out.println("===========================================================================");
		System.out.println("ETA: " + formatIntoHHMMSS(missingTime)
				+" ; MISSING: " + (totalRuns - runsCompleted) + " RUNS");
		System.out.println("===========================================================================");


	}

	public static String formatIntoHHMMSS(long l) {

		long hours = l / 3600,
				remainder = l % 3600,
				minutes = remainder / 60,
				seconds = remainder % 60;

		return ( (hours < 10 ? "0" : "") + hours
				+ "h" + (minutes < 10 ? "0" : "") + minutes
				+ "m" + (seconds< 10 ? "0" : "") + seconds + "s");
	}

	private MutationManager initialiseMutator(String genomeType, boolean geneDuplication) {
		if(genomeType.equalsIgnoreCase("basicgenome")){
			if(!geneDuplication)
				return new BasicMutatorManager(arguments);
			else
				return new BasicMutatorManagerWithDuplication(arguments);
		}
		else if(genomeType.equalsIgnoreCase("modulargenome")){
			return new ModuleMutatorManager(arguments);
		}
		return null;
	}

	private ListedPopulation initialisePopulation(String genomeType, int populationSize, int inputs, int outputs) {
		if(genomeType.equalsIgnoreCase("basicgenome")){
			return new BasicGenomeFactory().generateInitialPopulation(populationSize, inputs, outputs);
		}
		else if(genomeType.equalsIgnoreCase("modulargenome")){
			return new ModularGenomeFactory().generateInitialPopulation(populationSize, inputs, outputs);
		}

		return null;
	}

}
